/**
 * @file module.c
 * @brief function definitions for manipulating modules
 * @author Scott Moyers
 * @date 2008
 */

#include "module.h"
#include "util.h"
#include "limits.h"

#include <string.h>
#include <assert.h>

static void generate_input_pattern(struct Module *m, int n)
{
	int i, j;
	int w = signal_width(m->num_inputs);
	unsigned int *s = m->signals[n];
	int bit = 0;
	int flip = 1 << n;

	for(j = 0; j < w; j++) {
		s[j] = 0;
		for(i = 0; i < 32; i++) {
			s[j] |= bit << i;
			flip--;
			if (flip == 0) {
				bit = !bit;
				flip = 1 << n;
			}
		}
	}
	mask_signal(m->signals[n], m->num_inputs);
}

/**
 * generates input patterns for circuit simulation
 * input 0: 0xaaaaaaaa
 * input 1: 0xcccccccc
 * input 2: 0xf0f0f0f0 etc...
 */
static void generate_inputs(struct Module *m)
{
	unsigned int i;
	for(i = 0; i < m->num_inputs; i++) {
		generate_input_pattern(m, i);
	}
}

unsigned long next_module_id()
{
	static unsigned long id = 0;
	return id++;
}

struct Module *new_module(int id, int num_inputs, int num_gates)
{
	struct Module *m = (struct Module *) smalloc(sizeof(struct Module));
	m->id = id;
	m->num_inputs = num_inputs;
	m->num_gates = num_gates;
	m->circuit = (struct Gate *) smalloc(sizeof(struct Gate) * num_gates);
	m->signals = malloc_signals(num_signals(m), signal_width(m->num_inputs));
	generate_inputs(m);
	return m;
}

void free_module(struct Module *m)
{
	if (m != NULL) {
		free(m->circuit);
		free_signals(m->signals, num_signals(m));
		free(m);
	}
}

void execute_module(struct Module *m)
{
	unsigned int i;
	unsigned int **s = m->signals;
	unsigned int w = signal_width(m->num_inputs);
	for(i = 0; i < m->num_gates; i++) {
		unsigned int j;
		struct Gate *g = &m->circuit[i];
		switch (g->gate_type) {
		case AND2:
			for(j = 0; j < w; j++) {
				s[g->out][j] = s[g->in[0]][j] & s[g->in[1]][j];
			}
			break;
		case NAND2:
			for(j = 0; j < w; j++) {
				s[g->out][j] = ~(s[g->in[0]][j] & s[g->in[1]][j]);
			}
			break;
		case OR2:
			for(j = 0; j < w; j++) {
				s[g->out][j] = s[g->in[0]][j] | s[g->in[1]][j];
			}
			break;
		case NOR2:
			for(j = 0; j < w; j++) {
				s[g->out][j] = ~(s[g->in[0]][j] | s[g->in[1]][j]);
			}
			break;
		case XOR2:
			for(j = 0; j < w; j++) {
				s[g->out][j] = s[g->in[0]][j] ^ s[g->in[1]][j];
			}
			break;
		case XNOR2:
			for(j = 0; j < w; j++) {
				s[g->out][j] = ~(s[g->in[0]][j] ^ s[g->in[1]][j]);
			}
			break;
		case INV:
			for(j = 0; j < w; j++) {
				s[g->out][j] = ~(s[g->in[0]][j]);
			}
			break;
		case CONST_ZERO:
			for(j = 0; j < w; j++) {
				s[g->out][j] = 0;
			}
			break;
		case GATE_TYPES:
			assert(0);
			break;
		}
		mask_signal(m->signals[g->out], m->num_inputs);
	}
}

void colour_module(struct Module *m, enum Colour c)
{
	unsigned int i;
	for(i = 0; i < m->num_gates; i++) {
		m->circuit[i].colour = c;
	}
}

void colour_tree(struct Module *m, int n, enum Colour c)
{
	struct Gate *g = &m->circuit[n];
	g->colour = c;
	if (g->in[0] >= m->num_inputs) {
		colour_tree(m, g->in[0] - m->num_inputs, c);
	}

	if (g->in[1] >= m->num_inputs) {
		colour_tree(m, g->in[1] - m->num_inputs, c);
	}
}

void fprint_module(FILE *fp, struct Module *m)
{
	unsigned int i;
	for(i = 0; i < m->num_gates; i++) {
		unsigned int j;
		struct Gate *g = &m->circuit[i];
		for(j = 0; j < num_gate_inputs(g); j++) {
			fprintf(fp, "%5d ", g->in[j]);
			fprintf(fp, "[");
			fprint_signal(fp, m->signals[g->in[j]], signal_width(m->num_inputs));
			fprintf(fp, "]");
		}

		for(; j < MAX_NUM_INPUTS; j++) {
			unsigned int k;
			fprintf(fp, "          "); /* 10 spaces */
			for(k = 0; k < signal_width(m->num_inputs); k++) {
				fprintf(fp, "        "); /* 8 spaces */
			}
		}

		fprintf(fp, " %s -> %5d [", get_gate_name(g), g->out);
		fprint_signal(fp, m->signals[g->out], signal_width(m->num_inputs));
		fprintf(fp, "]");
		fprintf(fp, " level: %d\n", g->level);
	}
}

void fprint_signal(FILE *fp, unsigned int *s, int n)
{
	int i;
	fprintf(fp, "0x");
	for(i = n - 1; i >= 0; i--) {
		fprintf(fp, "%08x", s[i]);
	}
}

unsigned int **malloc_signals(unsigned int rows, unsigned int cols)
{
	unsigned int i;
	unsigned int** array;
	array = (unsigned int**) smalloc(rows * sizeof(unsigned int *));
	for (i = 0; i < rows; i++) {
		array[i] = (unsigned int *) smalloc(cols * sizeof(unsigned int));
	}
	return array;
}

void free_signals(unsigned int **sig, unsigned int rows)
{
	unsigned int i;
	for(i = 0; i < rows; i++) {
		free(sig[i]);
	}
	free(sig);
}

int cmp_signals(unsigned int a[], unsigned int b[], unsigned int w)
{
	return memcmp(a, b, w * sizeof(unsigned int));
}

int diff_pop_cnt(unsigned int a[], unsigned int b[], unsigned int w)
{
	int cnt = 0;
	unsigned int i;
	for(i = 0; i < w; i++) {
		if (a[i] != b[i]) {
			cnt += number_of_set_bits(a[i] ^ b[i]);
		}
	}
	return cnt;
}

struct Module *copy_module(struct Module const *m)
{
	unsigned int i;
	struct Module *cpy = new_module(m->id, m->num_inputs, m->num_gates);
	memcpy(cpy->circuit, m->circuit, sizeof(struct Gate) * m->num_gates);
	for(i = 0; i < num_signals(m); i++) {
		memcpy(cpy->signals[i], m->signals[i],
		       sizeof(unsigned int) * signal_width(m->num_inputs));
	}
	cpy->fitness = m->fitness;
	return cpy;
}

int min_signal_diff(struct Module *m, struct TruthTable *t)
{
	int diff = 0;
	unsigned int j;
	assert(m->num_inputs == t->nins);
	for(j = 0; j < t->nouts; j++) {
		unsigned int i;
		int min = INT_MAX;
		for(i = 0; i < num_signals(m); i++) {
			int cnt = diff_pop_cnt(m->signals[i], t->signals[j], signal_width(t->nins));
			if (cnt < min) {
				min = cnt;
				if (min == 0) {
					break;
				}
			}
		}
		diff += min;
	}
	return diff;
}

/**
 * updates the levels of each gate in the given
 * module. 0 is at an output and the level increments
 * as you get further away from it.
 * @param m module to update the levels for
 * @return longest path in the module
 */
static unsigned int update_levels(struct Module *m)
{
	int i;
	unsigned int max = 0;
	for(i = 0; i < (signed) m->num_gates; i++) {
		m->circuit[i].level = 0;
	}

	for(i = m->num_gates - 1; i >= 0; i--) {
		unsigned int j;
		struct Gate *g = &m->circuit[i];
		for(j = 0; j < num_gate_inputs(g); j++) {
			int gn = g->in[j] - m->num_inputs;
			if (gn >= 0) {
				struct Gate *gin = &m->circuit[gn];
				if (gin->level <= g->level) {
					gin->level = g->level + 1;
					if (gin->level > max) {
						max = gin->level;
					}
				}
			}
		}
	}
	return max;
}

unsigned int depth_module(struct Module *m)
{
	return update_levels(m);
}

int compare_module_fitness(void *value1, void *value2)
{
	struct Module *m1 = (struct Module *)value1;
	struct Module *m2 = (struct Module *)value2;
	if (m1->fitness > m2->fitness) {
		return -1;
	} else if (m1->fitness < m2->fitness) {
		return 1;
	} else {
		return 0;
	}
}

unsigned int is_cyclic(struct Module *m)
{
	unsigned int i;
	for(i = 0; i < m->num_gates; i++) {
		unsigned int j;
		struct Gate* g = &m->circuit[i];
		unsigned int inps = num_gate_inputs(g);
		for(j = 0; j < inps; j++) {
			if(g->in[j] >= i + m->num_inputs) {
				return 1;
			}
		}
	}
	return 0;
}

void layout_module(struct Module *m)
{
	int depth = update_levels(m);
	float *cnt = (float *) calloc(depth + 1, sizeof(float));
	unsigned int i;
	for(i = 0; i < m->num_gates; i++) {
		struct Gate *g = &m->circuit[i];
		/* allow space either side for ports */
		g->x = (float)(g->level + 1.0) / (depth + 2.0);
		g->y = cnt[g->level];
		cnt[g->level] += 1.0;
	}

	/* normalise y */
	for(i = 0; i < m->num_gates; i++) {
		struct Gate *g = &m->circuit[i];
		g->y /= cnt[g->level] + 1.0;
		g->y += 1.0 / (cnt[g->level] + 1.0);
	}

	free(cnt);
}
